package com.yeyks.common.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.TimeZone;

/**
 * @author fuchangshuai
 * @time 2018/12/26
 */
public class DateUtils {
    private static DateTimeFormatter yyyyMMddHHmmssformatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static DateTimeFormatter yyyyMMddformatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    private static DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
    private static DateTimeFormatter MMdd = DateTimeFormatter.ofPattern("MMdd");

    public static final SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取当前时间的后i天
     * 精确到秒
     *
     * @param i
     * @return
     */
    public static String getAddDayTime(int i) {
        Date date = new Date(System.currentTimeMillis() + i * 24 * 60 * 60 * 1000);
        return sdfTime.format(date);
    }

    /**
     * 获取当前时间的+多少秒
     * 精确到秒
     *
     * @param i
     * @return
     */
    public static String getAddDaySecond(int i) {
        Date date = new Date(System.currentTimeMillis() + i * 1000);
        return sdfTime.format(date);
    }

    /**
     * localDateTime转Date类型
     *
     * @param localDateTime
     * @return
     */
    private static Date localDateTimeToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * localDate转Date类型
     *
     * @param localDate
     * @return
     */
    public static Date localDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * date 转换成 LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * date 转换成 LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate dateToLocalDate(Date date) {
        return dateToLocalDateTime(date).toLocalDate();
    }

    /**
     * 将 yyyy-MM-dd 类型的转化日期格式
     *
     * @param dateStr
     * @return 日期
     */
    public static Date yyyyMMddToDate(String dateStr) {
        if (!ArgumentsCheckUtil.notEmpty(dateStr)) {
            return null;
        }
        LocalDate date = LocalDate.parse(dateStr);
        return DateUtils.localDateToDate(date);
    }

    /**
     * 将 yyyy/MM/dd 类型的转化日期格式
     * @param yyyyMMddformatter
     * @return
     */
    public static Date strXmlToDate(String yyyyMMddformatter){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = null;
        try {
            date = sdf.parse(yyyyMMddformatter);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 将 yyyy-MM-dd HH:mm:ss 类型的转化日期格式
     * @param yyyyMMddHHmmssformatter
     * @return
     */
    public static Date str14ToDate(String yyyyMMddHHmmssformatter) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse(yyyyMMddHHmmssformatter);
        return date;
    }


    /**
     * 将 yyyy-MM-dd HH:mm:ss 类型的转化日期格式
     *
     * @param dateStr
     * @return 日期
     */
    public static Date ymdhmsToDate(String dateStr) {
        if (!ArgumentsCheckUtil.notEmpty(dateStr)) {
            return null;
        }
        LocalDateTime time = ymdhmsToLocalDateTime(dateStr);
        return time == null ? null : localDateTimeToDate(time);
    }

    /**
     * 获取两个日期之间相差的天数
     *
     * @param begin
     * @param end
     * @return
     */
    public static Long getDayByBeginEnd(Date begin, Date end) {
        LocalDate beginLocalDate = dateToLocalDate(begin);
        LocalDate endLocalDate = dateToLocalDate(end);
        return endLocalDate.toEpochDay() - beginLocalDate.toEpochDay();
    }

    /**
     * 获取两个日期之间相差的天数
     *
     * @param begin
     * @param end
     * @return
     */
    public static int getDayByBeginEndToInt(Date begin, Date end) {
        return getDayByBeginEnd(begin, end).intValue();
    }

    /**
     * 获取两个日期之间相差的天数
     *
     * @param begin
     * @param end
     * @return
     */
    public static Long getDayStrToStr(String begin, String end) {
        Date beginDate = toDateTime(begin);
        Date endDate = toDateTime(end);
        return getDayByBeginEnd(beginDate, endDate);
    }

    /**
     * 获取两个日期之间相差的天数
     *
     * @param begin
     * @param end
     * @return
     */
    public static int getDayStrToInt(String begin, String end) {
        return getDayStrToStr(begin, end).intValue();
    }

    /**
     * 将LocalDateTime类型的转换成YYYYMMddHHmmss格式的字符串
     *
     * @param date
     * @return 如201910121244
     */
    public static String localDateTimeToYMDHMSStr(LocalDateTime date) {
        return yyyyMMddHHmmss.format(date);
    }

    /**
     * 将当前时间转换成YYYYMMddHHmmss格式的字符串
     *
     * @return 如20191012
     */
    public static String nowToYMDHMSStr() {
        return yyyyMMddHHmmss.format(LocalDateTime.now());
    }

    /**
     * 将当前时间转换成YYYYMMdd格式的字符串
     *
     * @return 如20191012
     */
    public static String nowToYMDStr() {
        return yyyyMMdd.format(LocalDateTime.now());
    }

    /**
     * 将当前时间转换成MMdd格式的字符串
     *
     * @return 如1012
     */
    public static String nowToMDStr() {
        return MMdd.format(LocalDateTime.now());
    }

    /**
     * 将date类型的转换成YYYYMMddHHmmss格式的字符串
     *
     * @param date
     * @return 如201910121244
     */
    public static String dateToYMDHMSStr(Date date) {
        return yyyyMMddHHmmss.format(dateToLocalDateTime(date));
    }

    /**
     * localDateTime转换成yyyy-MM-dd HH:mm:ss格式的字符串
     *
     * @param dateTime
     * @return
     */
    public static String formatYMDHMSStr(LocalDateTime dateTime) {
        return format(dateTime, yyyyMMddHHmmssformatter);
    }

    /**
     * 将LocalDateTime类型的转换成指定格式的字符串
     *
     * @param dateTime
     * @param formatter (如yyyy-MM-dd HH:mm:ss)
     * @return
     */
    private static String format(LocalDateTime dateTime, DateTimeFormatter formatter) {
        if (dateTime == null) {
            return null;
        }
        return formatter.format(dateTime);
    }

    /**
     * date转换成yyyy-MM-dd HH:mm:ss格式的字符串
     *
     * @param date
     * @return
     */
    public static String formatYMDHMSStr(Date date) {
        if (date == null) {
            return null;
        }
        return formatYMDHMSStr(dateToLocalDateTime(date));
    }

    /**
     * 将Date类型的转换成yyyy-MM-dd格式字符串
     *
     * @param date
     * @return
     */
    public static String toYYMMddStr(Date date) {
        if (date == null) {
            return null;
        }
        return localDateTimeToYMDStr(dateToLocalDateTime(date));
    }

    /**
     * 将LocalDateTime类型的转换成yyyy-MM-dd格式字符串
     *
     * @param dateTime
     * @return
     */
    public static String localDateTimeToYMDStr(LocalDateTime dateTime) {
        return yyyyMMddformatter.format(dateTime);
    }

    /**
     * 将yyyy-MM-dd HH:mm:ss格式的字符串转换成LocalDateTime类型的
     *
     * @param format (yyyy-MM-dd HH:mm:ss)
     * @return
     */
    public static LocalDateTime ymdhmsToLocalDateTime(String format) {
        if (!ArgumentsCheckUtil.notEmpty(format)) {
            return null;
        }
        return LocalDateTime.parse(format, yyyyMMddHHmmssformatter);
    }

    /**
     * 获取当前时间的分钟数
     *
     * @return 分钟
     */
    public static Integer getCurrentMinuteOfDay() {
        LocalDateTime now = LocalDateTime.now();
        return now.getHour() * 60 + now.getMinute();
    }

    /**
     * 判断LocalDateTime是否是今天
     *
     * @param time
     * @return
     */
    public static Boolean isToday(LocalDateTime time) {
        if (time == null) {
            return false;
        }
        LocalDate date = LocalDate.now();
        return date.isEqual(time.toLocalDate());
    }

    /**
     * 判断Date是否是今天
     *
     * @param date
     * @return
     */
    public static Boolean isToday(Date date) {
        if (date == null) {
            return false;
        }
        LocalDate today = LocalDate.now();
        return today.isEqual(dateToLocalDate(date));
    }

    /**
     * 判断date是否是昨天
     *
     * @param date
     * @return
     */
    public static Boolean isYesterday(Date date) {
        if (date == null) {
            return false;
        }
        LocalDate today = LocalDate.now().minusDays(1);
        return today.isEqual(dateToLocalDate(date));
    }

    /**
     * 获取date后几天的时间
     *
     * @param date
     * @param number
     * @return
     */
    public static Date dayAfter(Date date, Integer number) {
        return localDateTimeToDate(dateToLocalDateTime(date).plusDays(number));
    }

    /**
     * 对应当前n天前时间(0代表当前时间，1...表前n天，-1...表后n天)
     *
     * @param number
     * @return date
     */
    public static Date dayBefore(Integer number) {
        return localDateTimeToDate(LocalDateTime.now().minusDays(number));
    }

    /**
     * 对应当前n天前时间(0代表当前时间，1...表前n天，-1...表后n天)
     *
     * @param number
     * @return string
     */
    public static String dayBeforeToStr(Integer number) {
        return formatYMDHMSStr(dayBefore(number));
    }

    /**
     * 获取当天开始时间（0表示当前,-1...表前n天，1...表后n天）
     *
     * @param day
     * @return
     */
    public static Date dayBegin(Integer day) {
        return localDateTimeToDate(LocalDateTime.of(LocalDate.now().plusDays(day), LocalTime.MIN));
    }

    /**
     * 获取距离date前(或后)n天的开始时间（0表示当前,-1...表前n天，1...表后n天）
     *
     * @param day
     * @param date
     * @return
     */
    public static Date dayBegin(Date date, Integer day) {
        LocalDate localDate = dateToLocalDate(date);
        return localDateTimeToDate(LocalDateTime.of(localDate.plusDays(day), LocalTime.MIN));
    }

    /**
     * 获取当天开始时间（0表示当前,-1...表前n天，1...表后n天）
     *
     * @param day
     * @return (如 ： 2019 - 10 - 10 00 : 00 : 00)
     */
    public static String dayBeginToStr(Integer day) {
        return formatYMDHMSStr(dayBegin(day));
    }

    /**
     * 获取距当天前(或后)n天的结束时间（0表示当前,-1...表前n天，1...表后n天）
     *
     * @param day
     * @return
     */
    public static Date dayEnd(Integer day) {
        return localDateTimeToDate(LocalDateTime.of(LocalDate.now().plusDays(day), LocalTime.MAX));
    }

    /**
     * 获取距当天前(或后)n天的结束时间（0表示当前,-1...表前n天，1...表后n天）
     *
     * @param day
     * @return
     */
    public static String dayEndToStr(Integer day) {
        return formatYMDHMSStr(dayEnd(day));
    }

    /**
     * 获取昨天凌晨后n小时的时间
     *
     * @param hours
     * @return date
     */
    public static Date yesterdayAfterHour(Integer hours) {
        return localDateTimeToDate(LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MIN).plusHours(hours));
    }

    /**
     * 获取昨天凌晨后n小时的时间
     *
     * @param hours
     * @return (如2019 - 10 - 10 00 : 00 : 00)
     */
    public static String yesterdayAfterHourToStr(Integer hours) {
        return formatYMDHMSStr(yesterdayAfterHour(hours));
    }

    /**
     * 获取据现在后几分钟的时间
     *
     * @param minutes
     * @return date
     */
    public static Date nowAfterMinuteToDate(Integer minutes) {
        return localDateTimeToDate(LocalDateTime.now().plusMinutes(minutes));
    }

    /**
     * 获取据现在后几分钟的时间
     *
     * @param minutes
     * @return string
     */
    public static String nowAfterMinutesToStr(Integer minutes) {
        return formatYMDHMSStr(nowAfterMinuteToDate(minutes));
    }

    /**
     * 获取据date后几分钟的时间
     *
     * @param minutes
     * @return date
     */
    public static Date dateAfterMinute(Date date, Integer minutes) {
        LocalDateTime localDateTime = DateUtils.dateToLocalDateTime(date);
        return localDateTimeToDate(localDateTime.plusMinutes(minutes));
    }

    /**
     * 根据指定的yyyy-MM-dd HH:mm:ss类型的字符串转换成n小时后的date类型
     *
     * @return date
     */
    public static Date StrAfterHoursToDate(String time, Integer hoursNum) {
        if (!ArgumentsCheckUtil.notEmpty(time)) {
            return null;
        }
        LocalDateTime localDateTime = ymdhmsToLocalDateTime(time);
        if (localDateTime == null) {
            return null;
        }
        return localDateTimeToDate(localDateTime.plusHours(hoursNum));
    }

    /**
     * 根据指定的yyyy-MM-dd HH:mm:ss类型的字符串转换成n小时后的yyyy-MM-dd HH:mm:ss类型
     *
     * @return date
     */
    public static String strAfterHoursToStr(String time, Integer hoursNum) {
        if (!ArgumentsCheckUtil.notEmpty(time)) {
            return null;
        }
        LocalDateTime localDateTime = ymdhmsToLocalDateTime(time);
        if (localDateTime == null) {
            return null;
        }
        return formatYMDHMSStr(localDateTime.plusHours(hoursNum));
    }

    /**
     * 判断据date后hoursNum小时的时间
     *
     * @param date
     * @param hoursNum
     * @return date
     */
    public static Date afterHoursToDate(Date date, Long hoursNum) {
        LocalDateTime localDateTime = DateUtils.dateToLocalDateTime(date);
        return localDateTimeToDate(localDateTime.plusHours(hoursNum));
    }

    /**
     * 判断据date后dayNum天的时间
     *
     * @param date
     * @param dayNum
     * @return date
     */
    public static Date afterDaysToDate(Date date, Long dayNum) {
        LocalDateTime localDateTime = DateUtils.dateToLocalDateTime(date);
        return localDateTimeToDate(localDateTime.plusDays(dayNum));
    }

    /**
     * 根据指定的yyyy-MM-dd HH:mm:ss类型的字符串转换成n小时后的yyyy-MM-dd HH:mm:ss类型
     *
     * @return date
     */
    public static String strAfterDaysToStr(String time, Integer dayNum) {
        if (!ArgumentsCheckUtil.notEmpty(time)) {
            return null;
        }
        LocalDateTime localDateTime = ymdhmsToLocalDateTime(time);
        if (localDateTime == null) {
            return null;
        }
        return formatYMDHMSStr(localDateTime.plusDays(dayNum));
    }

    /**
     * 判断据date后hoursNum小时的时间
     *
     * @param date
     * @param hoursNum
     * @return string
     */
    public static String afterHoursToString(Date date, Long hoursNum) {
        return formatYMDHMSStr(afterHoursToDate(date, hoursNum));
    }

    /**
     * 判断当前时间的小时是否大于hour
     *
     * @return
     */
    public static Boolean afterHour(Integer hour) {
        return LocalDateTime.now().getHour() >= hour;
    }

    /**
     * 获取当前的小时数
     *
     * @return
     */
    public static Integer getNowHour() {
        return LocalDateTime.now().getHour();
    }

    /**
     * 比较dateOne是否大于等于dateTwo
     *
     * @return
     */
    public static Boolean dateCompareDate(Date dateOne, Date dateTwo) {
        return dateOne.compareTo(dateTwo) >= 0;
    }

    /**
     * 将时间戳转换为date类型
     *
     * @param time
     * @return
     */
    public static Date stampToDate(String time) {
        long lt = new Long(time);
        return new Date(lt);
    }

    /**
     * 将时间戳转换为yyyy-MM-dd HH:mm:ss类型的字符串
     *
     * @param time
     * @return yyyy-MM-dd HH:mm:ss
     */
    public static String stampToYMDHMS(String time) {
        return formatYMDHMSStr(stampToDate(time));
    }

    /**
     * 将时间戳转换成yyyy-MM-dd格式的date类型
     *
     * @param dateStr
     * @return 日期
     */
    public static Date stampToYMDDate(Long dateStr) {
        if (dateStr == null || dateStr == 0) {
            return null;
        }
        Date stampDate = new Date(dateStr);
        LocalDate date = dateToLocalDate(stampDate);
        return DateUtils.localDateToDate(date);
    }

    /**
     * 将时间戳转换成yyyy-MM-dd HH:mm:ss格式的date类型
     *
     * @param dateStr
     * @return 日期
     */
    public static Date stampToYMDHMSDate(Long dateStr) {
        if (dateStr == null || dateStr == 0) {
            return null;
        }
        return new Date(dateStr);
    }

    /**
     * 将时间戳转换成yyyy-MM-dd格式
     *
     * @param dateStr
     * @return 日期
     */
    public static String stampToYMDStr(Long dateStr) {
        if (dateStr == null || dateStr == 0) {
            return null;
        }
        LocalDateTime localDateTime = DateUtils.dateToLocalDateTime(new Date(dateStr));
        return localDateTimeToYMDStr(localDateTime);
    }

    /**
     * date 转换成 LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 将date类型的转换成 yyyy-MM-dd 日期格式
     *
     * @param date
     * @return
     */
    public static String dateToyyyyMMdd(Date date) {
        LocalDate localDate = toLocalDateTime(date).toLocalDate();
        return localDateToYMD(localDate);
    }

    /**
     * 将LocalDate类型的转化成yyyy-MM-dd的字符串格式
     *
     * @param localDate
     * @return
     */
    public static String localDateToYMD(LocalDate localDate) {
        return yyyyMMddformatter.format(localDate);
    }

    public static void main(String[] args) {
        System.out.println(getFirstDateOfMonth(new Date()));
        System.out.println(getLastDateOfMonth(new Date()));
//        LocalDate now = LocalDate.now();
//        Date dateNow = localDateToDate(now);
//        String strNow = DateUtils.toYYMMddStr(dateNow);
//        Random random = new Random();
//        for (int i = 0; i < 1000; i++) {
//            //随机1 - 30
//            LocalDate randomLocalDate = LocalDate.now().plusDays(random.nextInt(30) + 1);
//            Date randomDate = DateUtils.localDateToDate(randomLocalDate);
//            String strRandom = DateUtils.toYYMMddStr(randomDate);
//            Long day = DateUtils.getDayByBeginEnd(dateNow, randomDate);
//            System.out.println(strNow + "入住到" + strRandom + "结束一共" + day + "天");
//        }
    }

    /**
     * 将 yyyy-MM-dd HH:mm:ss 类型的转化日期格式
     *
     * @param dateStr
     * @return 日期
     */
    public static Date toDateTime(String dateStr) {
        if (!ArgumentsCheckUtil.notEmpty(dateStr)) {
            return null;
        }
        LocalDateTime time = parseLocalDateTime(dateStr);
        return time == null ? null : DateUtils.toDate(time);
    }

    public static LocalDateTime parseLocalDateTime(String format) {
        if (!ArgumentsCheckUtil.notEmpty(format)) {
            return null;
        }
        return LocalDateTime.parse(format, yyyyMMddHHmmssformatter);
    }

    /**
     * localDateTime转Date类型
     *
     * @param localDateTime
     * @return
     */
    private static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * localDate转Date类型
     *
     * @param localDate
     * @return
     */
    public static Date toDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    public static String formatDate(Date date) {
        if (date == null) {
            return null;
        }
        return formatDate(toLocalDateTime(date));
    }

    public static String formatDate(LocalDateTime dateTime) {
        return yyyyMMddformatter.format(dateTime);
    }

    public static String getDateForDay(int i){
        String time = "0-0-0";
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            Calendar now = Calendar.getInstance();
            now.add(Calendar.DAY_OF_MONTH, i);
            time = sdf.format(now.getTime());
        }catch (Exception e){
            e.printStackTrace();
        }
        return time;
    }

    /**
     * 判断两个时间段是否有重合,不包含起止时间点
     *
     * @param strDate1 开始时间1
     * @param endDate1 结束时间1
     * @param strDate2 开始时间2
     * @param endDate2 结束时间2
     * @return
     */
    public static Boolean determineIfThereIsACoincidenceBetweenTheTwoTimePeriodsDoesNotIncludeBothSides(
            Date strDate1, Date endDate1, Date strDate2, Date endDate2) {
        if (
                isInDoesNotIncludeBothSides(strDate1, strDate2, endDate2)
                        || isInDoesNotIncludeBothSides(endDate1, strDate2, endDate2)
                        || isInDoesNotIncludeBothSides(strDate2, strDate1, endDate1)
                        || isInDoesNotIncludeBothSides(endDate2, strDate1, endDate1)
                ) {
            return true;
        }
        return false;
    }

    /**
     * 当前日期是否在日期指定范围内<br>
     * 不包含两端时间点<br>
     * 起始日期和结束日期可以互换
     *
     * @param date      被检查的日期
     * @param beginDate 起始日期
     * @param endDate   结束日期
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isInDoesNotIncludeBothSides(Date date, Date beginDate, Date endDate) {
        long beginMills = beginDate.getTime() + 1;
        long endMills = endDate.getTime() - 1;
        long thisMills = date.getTime();
        return thisMills >= Math.min(beginMills, endMills) && thisMills <= Math.max(beginMills, endMills);
    }

    /**
     * 取得月第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDateOfMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
        return c.getTime();
    }

    /**
     * 取得月最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDateOfMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
        return c.getTime();
    }

    /**
     * 清空时分秒
     *
     * @param date 日期实例
     * @return
     */
    public static Date setTimeToZero(Date date) {
        if (date == null) {
            return date;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    /**
     * 补全十分秒为23：59：59
     * @param date 当前时间
     * @return 返回结果
     */
    public static Date setTimeToMax(Date date) {
        if (date == null) {
            return date;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

}
